﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TrackSystem.Contracts;
using TrackSystem.Entities.Extensions;
using TrackSystem.Entities.Models;
using TrackSystem.IO.Data;
using TrackSystem.IO.Formula;
using TrackSystem.IO.Utils;

namespace TrackSystem.IO.Loops
{
    /// <summary>
    /// 调节回路类
    /// </summary>
    public class LoopItem
    {
        #region 变量声明
        /// <summary>
        /// 通道名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        ///  设定值
        /// </summary>
        public double SV { get; set; }
        /// <summary>
        /// 实际值
        /// </summary>
        public double PV { get; set; }
        /// <summary>
        /// 输出值
        /// </summary>
        public double OP { get; set; }
        /// <summary>
        /// 上一次的输出值
        /// </summary>
        public double lastOP { get; set; }
        /// <summary>
        /// 是否手动方式
        /// </summary>
        public bool Manual { get; set; }
        /// <summary>
        /// 上次是否是手动模式
        /// </summary>
        public bool lastManual { get; set; }
        /// <summary>
        /// 比例系数
        /// </summary>
        public double PVal { get; set; }
        /// <summary>
        /// 积分系数
        /// </summary>
        public double IVal { get; set; }
        /// <summary>
        /// 微分系数
        /// </summary>
        public double DVal { get; set; }
        /// <summary>
        /// 输出上限
        /// </summary>
        public double ULmt { get; set; }
        /// <summary>
        /// 输出下限
        /// </summary>
        public double LLmt { get; set; }
        /// <summary>
        /// 控制方式，目前保留
        /// </summary>
        public LoopMethod Methdod { get; set; }
        /// <summary>
        ///  控制是否使能
        /// </summary>
        public bool Enabled { get; set; }
        /// <summary>
        /// 正方向死区控制
        /// </summary>
        public int PDeadZone { get; set; }
        /// <summary>
        /// 负方向的死区
        /// </summary>
        public int NDeadZone { get; set; }
        /// <summary>
        /// 比咧系数
        /// </summary>
        public double Ratio { get; set; }
        /// <summary>
        /// 控制回路刷新周期，单位ms
        /// </summary>
        public int Interval { get; set; }
        /// <summary>
        ///  实际运行时间
        /// </summary>
        public int Cycle { get; set; }
        /// <summary>
        /// 时间计算时间
        /// </summary>
        public int Lapse { get; set; }
        /// <summary>
        /// 作用方式
        /// </summary>
        public bool Direction { get; set; }
        
        /// <summary>
        /// 装备的ID号
        /// </summary>
        public int EquipId { get; set; }

        /// <summary>
        /// 设置参数
        /// </summary>
        public string Params { get; set; }

        // Setting参数标准
        /// <summary>
        /// 先进PID控制算法标准 0-未使能  1-变速积分
        /// </summary>
        public int EnabledAdvanced { get; set; }

        /// <summary>
        /// 变速积分开启的偏差值1，大于该偏差，则为取消积分环节
        /// </summary>
        public double DeltaPoint1 { get; set; }

        /// <summary>
        /// 变速积分开启的偏差值2，小于该偏差，则开启全部积分环节
        /// </summary>
        public double DeltaPoint2 { get; set; }

        /// <summary>
        /// 自整定功能设置  0-未启动  1-开始 2-正在运行 3-完成, 10-100: 启动，输出为10%-100%
        /// </summary>
        public int AutoTuned { get; set; }

        //-------------------------------------------------------------------------
        public string SVName { get; set; }                  // 设定值寄存器名称
        public string PVName { get; set; }                  // 实际值寄存器名称
        public string OPName { get; set; }                  // 输出值寄存器名称
        public string Setting { get; set; }                  // 设定系数

        private double pid_delta, delta, delta1, delta2;
        private double vOut1;                   // 输出暂存值
        private double pid_max, pid_min;        // 积分上限、积分下限
        private double deadzone_p, deadzone_n;  // 死区上限、死区下限
        private double tunedOutputPrecent;         // 整定输出百分比 0.1-1

        private double nFirstOut;
        private double SegmaDelta;

        private Thread thd;
        private bool Running;
        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        public LoopItem()
        {
            Enabled = false;
            Running = false;    //初始不启动运行
            Interval = 100;     //时间 间隔为100ms
            delta = 0d;
            delta1 = 0d;
            delta2 = 0d;
            AutoTuned = 0;
        }

        /// <summary>
        /// 初始化化函数
        /// </summary>
        /// <param name="loopItem"></param>
        public void Init(LoopTable loopItem)
        {
            Enabled = false;
            if (Running)
            {
                Running = false;
                thd.Join();
            }

            Name = loopItem.Name;
            SVName = loopItem.SVName;
            PVName = loopItem.PVName;
            OPName = loopItem.OPName;
            Manual = loopItem.Action;
            lastManual = Manual;
            Methdod = loopItem.Method;
            Direction = loopItem.Dirction;

            PDeadZone = loopItem.PDeadZone;
            NDeadZone = loopItem.NDeadZone;
            Ratio = loopItem.Ratio;

            PVal = loopItem.Params_P;
            IVal = loopItem.Params_I;
            DVal = loopItem.Params_D;
            ULmt = loopItem.OutHLimit;
            LLmt = loopItem.OutLLimit;
            EquipId = loopItem.equipId;

            pid_max = (Ratio == 0) ? ULmt : ULmt / Ratio;
            pid_min = (Ratio == 0) ? LLmt : LLmt / Ratio;

            // 设定参数处理
            Setting = loopItem.Setting;
            AnaylsisParameter(Setting);

            deadzone_p = PDeadZone * ULmt / 100d;        // PDeadZone--》上限的百分比
            deadzone_n = NDeadZone * LLmt / 100d;     // PDeadZone--》上限的百分比

            thd = new Thread(PIDWorkerThread);
            Running = true;
            Enabled = true;
            thd.IsBackground = true;
            thd.Start();

        }

        /// <summary>
        /// 初始化参数
        /// </summary>
        private void InitParams()
        {
            OP = 0.0d;
            vOut1 = 0.0d;
            nFirstOut = 10;
            delta1 = 0.0;
            delta2 = 0.0;
            SegmaDelta = 0;
        }

        /// <summary>
        /// 参数分析程序
        /// 标准格式：VariabledIntegral=10,5; / Cylinder=10,5;
        /// </summary>
        /// <param name="sParams"></param>
        private void AnaylsisParameter(string sParams)
        {
            EnabledAdvanced = 0;

            if (string.IsNullOrEmpty(sParams))
            {
                return;
            }
            // Step 1   去掉字符串的空格
            sParams = sParams.Replace(" ", "");

            // Step 2  默认以分号隔开各个参数
            string[] arParams = sParams.Split(';');

            foreach (string ParamsLine in arParams)
            {
                string[] arItem = ParamsLine.Split('=');
                if (arItem.Length != 2) continue;

                string[] arCoefficient = arItem[1].Split(',');
                if (arCoefficient.Length == 2)
                {

                    DeltaPoint1 = 10;
                    DeltaPoint2 = 5;
                    if (double.TryParse(arCoefficient[0], out var d1))
                    {
                        DeltaPoint1 = Math.Abs(d1);
                    }

                    if (double.TryParse(arCoefficient[1], out var d2))
                    {
                        DeltaPoint2 = Math.Abs(d2);
                        if (DeltaPoint2 == 0) DeltaPoint2 = 5;
                    }

                    if (arItem[0] == "VariabledIntegral")
                    {
                        EnabledAdvanced = 1;
                    }

                    if (arItem[0] == "Cylinder")
                    {
                        EnabledAdvanced = 2;
                    }
                }
            }
        }

        /// <summary>
        /// 析构函数
        /// </summary>
        public void Dispose()
        {
            Enabled = false;
            if (Running)
            {
                Running = false;
                thd.Join();
            }
        }

        /// <summary>
        /// PID条件线程
        /// </summary>
        private void PIDWorkerThread()
        {
            double pid_out;

            int startTicks, deltaTicks, temp;
            int lastTicks = Environment.TickCount;
            bool bLastDirection = Direction;

            AutoTunePackage autoTune = new(this);
            bool bCanTuned = false;

            while (Running)
            {
                if (!Enabled)
                {
                    Thread.Sleep(Interval);
                    continue;
                }

                startTicks = Environment.TickCount;

                // 获取设定值，实际值
                OperandDef svOp = RegFactory.Instance.GetVal(SVName);
                SV = svOp.DataType == DataTypeEnum.INTEGER? svOp.iVal : svOp.dVal;
                OperandDef pvOp = RegFactory.Instance.GetVal(PVName);
                PV = pvOp.DataType == DataTypeEnum.INTEGER ? pvOp.iVal : pvOp.dVal;

                #region 参数准备阶段 -------------------
                // 现在为手动，以前为自动方式
                if (!lastManual && Manual)
                {
                    InitParams();
                }
                lastManual = Manual;

                // 作用方式发生改变
                if (Direction != bLastDirection)
                {
                    bLastDirection = Direction;
                    InitParams();
                }
                #endregion

                #region 自整定相关参数调整 -----------
                // 退出自整定模式
                if (autoTune.TuneEnabled > 0 && AutoTuned == 0)
                {
                    autoTune.TuneEnabled = 0;
                }

                // 自整定结束， 转入自动方式
                if (AutoTuned >= 10 && AutoTuned <= 100)
                {
                    autoTune.Reset();
                    tunedOutputPrecent = AutoTuned / 100.0d;
                    autoTune.TuneEnabled = 1;
                    autoTune.OutputStep = (ULmt - LLmt) * tunedOutputPrecent;
                    AutoTuned = 2;
                    bCanTuned = false;
                    SegmaDelta = 0;
                }

                if (AutoTuned == 0 && autoTune.TuneEnabled > 0)
                {
                    autoTune.TuneEnabled = 0;
                }

                #endregion

                // 自动控制方式下 -------------------------
                if (!Manual)
                {
                    // 自整定方式 ------------------------
                    if (autoTune.TuneEnabled == 1 && bCanTuned)
                    {
                        if (autoTune.RelayFeedbackAutoTuning() == 0)
                        {
                            OP = Direction ? LLmt * tunedOutputPrecent : ULmt * tunedOutputPrecent;
                        }
                        else
                        {
                            OP = Direction ? ULmt * tunedOutputPrecent : LLmt * tunedOutputPrecent;
                        }
                    }
                    else  // PID控制方式 ------------------------
                    {
                        if (autoTune.TuneEnabled == 2)  // 自整定完成，修改PID参数
                        {
                            PVal = autoTune.Kp;
                            IVal = autoTune.Ki;
                            DVal = autoTune.Kd;
                            autoTune.TuneEnabled = 3;
                            AutoTuned = 3;
                        }

                        if (autoTune.TuneEnabled == 1 && Math.Abs(SV - PV) < 5.0)  // 是否到达设定值附近
                        {
                            bCanTuned = true;
                        }

                        delta = SV - PV;                    // 偏差计算
                        pid_out = EnabledAdvanced switch
                        {
                            1 => VariablePID(),     // 可变积分
                            2 => PositionPID(),     // 位置式PID
                            _ => IncrementalPID(),  // 增量式PID
                        };

                        // 如果发生超调 限幅
                        pid_out = Tools.RangeTrunc(pid_out, pid_max, pid_min);

                        // 保存上两次的误差值
                        delta2 = delta1;
                        delta1 = delta;

                        // 结果输出 死区调整
                        OP = PDeadZoneAdjust(pid_out);
                    }
                }

                // 输出到变量中 --------------------------
                RegFactory.Instance[OPName] = new OperandDef(OP);

                // ------运行时间处理---------------------------------------------
                #region 线程运行时间计算
                temp = Environment.TickCount;
                Cycle = temp - lastTicks;
                Lapse = temp - startTicks;
                lastTicks = temp;
                deltaTicks = Interval - Lapse;

                if (deltaTicks > 0)
                    Thread.Sleep(deltaTicks);
                #endregion
            }
        }

        /// <summary>
        /// 增量式PID计算
        /// </summary>
        /// <returns></returns>
        private double IncrementalPID()
        {
            pid_delta = PVal * (delta - delta1) + IVal * delta + DVal * (delta - 2 * delta1 + delta2);
            return (Direction) ? vOut1 + pid_delta : vOut1 - pid_delta;
        }

        /// <summary>
        /// 可变积分PID运算
        /// </summary>
        /// <returns></returns>
        private double VariablePID()
        {
            // 偏差超过DeltaPoint1 只有积分作用
            double absDelta = Math.Abs(delta);

            if (absDelta > DeltaPoint1)
            {
                pid_delta = PVal * delta;
            }
            else
            {
                double Parameter_I = IVal;
                if (absDelta <= DeltaPoint1 && absDelta >= DeltaPoint2)
                {
                    Parameter_I = IVal * (DeltaPoint1 - absDelta + DeltaPoint2) / DeltaPoint1;
                }
                pid_delta = PVal * (delta - delta1) + Parameter_I * delta + DVal * (delta - 2 * delta1 + delta2);
            }

            return (Direction) ? vOut1 + pid_delta : vOut1 - pid_delta;
        }

        /// <summary>
        /// 位置式PID
        /// </summary>
        /// <returns></returns>
        private double PositionPID()
        {
            SegmaDelta += delta;

            // 正作用方向  最大输出时 偏差为正  最小输时, 偏差为负 输出饱和，减少积分作用
            if (Direction)
            {
                if (Math.Abs(vOut1 - pid_max) < double.Epsilon && delta > 0)
                    SegmaDelta -= delta;
                if (Math.Abs(vOut1 - pid_min) < double.Epsilon && delta < 0)
                    SegmaDelta -= delta;
            }

            // 反作用方向  最大输出时 偏差为负  最小输时, 偏差为正 输出饱和，减少积分作用
            else
            {
                if (Math.Abs(vOut1 - pid_max) < double.Epsilon && delta < 0)
                    SegmaDelta -= delta;
                if (Math.Abs(vOut1 - pid_min) < double.Epsilon && delta > 0)
                    SegmaDelta -= delta;
            }

            // 偏差过大，取消积分作用
            if (Math.Abs(delta) > DeltaPoint2)
            {
                SegmaDelta = 0;
            }

            pid_delta =
                ((delta > 0) ? PVal : DeltaPoint1) * delta +                            // 比列
                SegmaDelta * IVal * Math.Abs((DeltaPoint2 - delta) / DeltaPoint2) +     // 积分 变积分
                DVal * (delta - delta1);                                                // 微分
            return (Direction) ? pid_delta : -pid_delta;
        }

        /// <summary>
        /// 死区调整
        /// </summary>
        /// <param name="opVal"></param>
        /// <returns></returns>
        private double PDeadZoneAdjust(double opVal)
        {
            // 当调节器输出发生过零，采用2倍死区输出的冲击输出 持续0.5s
            if ((vOut1 <= 0 && opVal > 0) || (vOut1 >= 0 && opVal < 0))
            {
                nFirstOut = 3;
            }

            vOut1 = opVal;                  // 保存调节结果                        
                                            // 前10次自动控制循环过程采用比较大的死区范围，之后采用比较小的死区范围
                                            // 死区控制    前十次启动是后面死区量的2倍
            if (nFirstOut > 0)
            {
                nFirstOut--;
            }

            // 死区功能开启
            // 当 OP > 0  需要把OP值[0-uLmt]映射到[PDeadZone_u-ULmt]中去  前几个循环，采用倍数死区
            if (PDeadZone > 0 && opVal > 0)
            {
                double temp_Dead = (nFirstOut > 0 && EnabledAdvanced == 2) ? 2 * deadzone_p : deadzone_p;
                return (ULmt - temp_Dead) / ULmt * opVal + temp_Dead;
            }

            // 当 OP < 0  需要把OP值[0-lLmt]映射到[PDeadZone_l-LLmt]中去
            //if (NegPDeadZone > 0 && opVal < 0)
            //{
            //    double temp_Dead = (nFirstOut > 0 && EnabledAdvanced == 2) ? 2 * PDeadZone_l : PDeadZone_l;
            //    return (LLmt - temp_Dead) / LLmt * opVal + temp_Dead;
            //}

            if (NDeadZone > 0 && opVal < 0)
            {
                double temp_Dead = (nFirstOut > 0 && EnabledAdvanced == 2) ? -2 * deadzone_n : -deadzone_n;
                return (LLmt - temp_Dead) / LLmt * opVal + temp_Dead;
            }

            // 其他情况保存不变
            return opVal;
        }

        /// <summary>
        /// 模糊控制线程
        /// </summary>
        private void FuzzyWorkThred()
        {
            int startTicks, deltaTicks, temp;
            int lastTicks = Environment.TickCount;
            bool bLastDirection = Direction;

            while (Running)
            {
                #region  循环运行开始处理
                if (!Enabled)
                {
                    Thread.Sleep(Interval);
                    continue;
                }
                startTicks = Environment.TickCount;
                #endregion

                #region 参数准备阶段 -------------------
                // 现在为手动，以前为自动方式
                if (!lastManual && Manual)
                {
                    InitParams();
                }
                lastManual = Manual;

                // 作用方式发生改变
                if (Direction != bLastDirection)
                {
                    bLastDirection = Direction;
                    InitParams();
                }
                #endregion
                // 获取设定值，实际值
                SV = RegFactory.Instance.GetVal(SVName).dVal;
                PV = RegFactory.Instance.GetVal(PVName).dVal;

                delta = SV - PV;                        // 偏差
                double delta_2 = delta - delta1;        // 偏差变化量

                #region 线程运行时间计算
                temp = Environment.TickCount;
                Cycle = temp - lastTicks;
                Lapse = temp - startTicks;
                lastTicks = temp;
                deltaTicks = Interval - Lapse;

                if (deltaTicks > 0)
                    Thread.Sleep(deltaTicks);
                #endregion
            }
        }

        /// <summary>
        /// 设置控制回路中的值
        /// </summary>
        /// <param name="index">索引号</param>
        /// <param name="val">设定值</param>
        public void SetVal(int index, OperandDef val)
        {
            switch (index)
            {
                case 0: Enabled = val.bVal; break;
                case 1: Manual = val.bVal; break;
                case 2: Direction = val.bVal; break;
                case 3: PVal = val.DataType == DataTypeEnum.DOUBLE ? val.dVal : val.iVal; break;
                case 4: IVal = val.DataType == DataTypeEnum.DOUBLE ? val.dVal : val.iVal; ; break;
                case 5: DVal = val.DataType == DataTypeEnum.DOUBLE ? val.dVal : val.iVal; ; break;
                case 6: ULmt = val.DataType == DataTypeEnum.DOUBLE ? val.dVal : val.iVal; ; break;
                case 7: LLmt = val.DataType == DataTypeEnum.DOUBLE ? val.dVal : val.iVal; ; break;
                case 8: Ratio = val.DataType == DataTypeEnum.DOUBLE ? val.dVal : val.iVal; ; break;
                case 9: PDeadZone = val.DataType == DataTypeEnum.DOUBLE ? (int)val.dVal : val.iVal; ; break;
                case 10: NDeadZone = val.DataType == DataTypeEnum.DOUBLE ? (int)val.dVal : val.iVal; break;
                case 11: OP = val.DataType == DataTypeEnum.DOUBLE ? val.dVal : val.iVal; ; break;
                case 14: EnabledAdvanced = val.DataType == DataTypeEnum.DOUBLE ? (int)val.dVal : val.iVal; ; break;
                case 15: DeltaPoint1 = val.DataType == DataTypeEnum.DOUBLE ? val.dVal : val.iVal; ; break;
                case 16: DeltaPoint2 = val.DataType == DataTypeEnum.DOUBLE ? val.dVal : val.iVal; ; break;
                case 17: AutoTuned = val.DataType == DataTypeEnum.DOUBLE ? (int)val.dVal : val.iVal; ; break;
                case 18: Setting = val.sVal ??= ""; break;
            }
            // ULmt >= 0 判断
            if (ULmt < 0) ULmt = 0;

            // LLmt <= 0 判断
            if (LLmt > 0) LLmt = 0;

            // 死区为0-100
            PDeadZone = (PDeadZone > 100) ? 100 : (PDeadZone < 0) ? 0 : PDeadZone;
            NDeadZone = (NDeadZone > 100) ? 100 : (NDeadZone < 0) ? 0 : NDeadZone;
            deadzone_p = PDeadZone * ULmt / 100d;
            deadzone_n = NDeadZone * LLmt / 100d;

            // 计算
            pid_max = (Ratio == 0) ? ULmt : ULmt / Ratio;
            pid_min = (Ratio == 0) ? LLmt : LLmt / Ratio;

            // 参数一致性检测
            if (EnabledAdvanced == 1)
            {
                if (DeltaPoint1 < 0) DeltaPoint1 = -DeltaPoint1;
                if (DeltaPoint2 < 0) DeltaPoint2 = -DeltaPoint2;
                if (DeltaPoint1 == 0) DeltaPoint1 = 10;
                if (DeltaPoint2 == 0) DeltaPoint2 = DeltaPoint1 / 2;
                if (DeltaPoint1 < DeltaPoint2) DeltaPoint2 = DeltaPoint1 / 2;
            }
            else if (EnabledAdvanced == 2)
            {
                if (DeltaPoint2 == 0) DeltaPoint2 = 5;
            }
        }

        /// <summary>
        /// 根据索引号获取控制回路的值
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public OperandDef GetVal(int index)
        {
            switch (index)
            {
                case 0: return new OperandDef(Enabled);
                case 1: return new OperandDef(Manual);
                case 2: return new OperandDef(Direction); 
                case 3: return new OperandDef(PVal);
                case 4: return new OperandDef(IVal);
                case 5: return new OperandDef(DVal);
                case 6: return new OperandDef(ULmt);
                case 7: return new OperandDef(LLmt);
                case 8: return new OperandDef(Ratio);
                case 9: return new OperandDef(PDeadZone);
                case 10: return new OperandDef(NDeadZone);
                case 11: return new OperandDef(OP);
                case 12: return new OperandDef(SV);
                case 13: return new OperandDef(PV);
                case 14: return new OperandDef(EnabledAdvanced);
                case 15: return new OperandDef(DeltaPoint1);
                case 16: return new OperandDef(DeltaPoint2);
                case 17: return new OperandDef(AutoTuned);
                case 18: return new OperandDef(Setting);
            }
            return new OperandDef();
        }

        /// <summary>
        /// 将目前的数据保存到数据库中
        /// </summary>
        /// <param name="wrapper"></param>
        public async Task SaveDataToTable(IRepositoryWrapper wrapper)
        {
            var loop = await wrapper.Loop.FirstOrDefaultAsync(a => a.Name == Name);
            if (loop != null)
            {
                loop.Params_P = PVal;
                loop.Params_I = IVal;
                loop.Params_D = DVal;
                loop.OutHLimit = ULmt;
                loop.OutLLimit = LLmt;
                loop.PDeadZone = PDeadZone;
                loop.NDeadZone = NDeadZone;
                loop.Dirction = Direction;
                loop.Ratio = Ratio;
                loop.Action = Manual;

                if (EnabledAdvanced == 1)
                {
                    loop.Setting = "VariabledIntegral = " + $"{DeltaPoint1}, {DeltaPoint2};";
                }
                else if (EnabledAdvanced == 2)
                {
                    loop.Setting = "Cylinder = " + $"{DeltaPoint1}, {DeltaPoint2};";
                }

                await wrapper.Loop.UpdateAsync(loop);
                await wrapper.SaveAsync();
            }
        }
    }
}
